using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;

namespace DataBroker.Dao
{
    /// <summary>
    /// Summary description for DALPropertyDescriptor.
    /// </summary>
    /// <summary>
    /// This class replaces the standard property descriptor in the sense that
    /// it will look for the "#" marker on property signatures, and navigate through
    /// the appropriate items to get/set the properties.
    /// </summary>
    internal class EntityPropertyDescriptor : PropertyDescriptor
    {
        private PropertyInfo _propInfo;
        private string _Category;
        private string _Description;
        private bool _IsBrowsable;
        private bool _CanWrite;
        private Type _DeclaringType;
        private Type _PropertyType;

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="propertyName">Property name</param>
        public EntityPropertyDescriptor(string propertyName) : 
            base(propertyName, null)
        {
        }


        private void SetProperties()
        {
            object[] atts = _propInfo.GetCustomAttributes(typeof(CategoryAttribute), true);
            _Category = atts.Length > 0 ? ((CategoryAttribute)atts[0]).Category : "General";
            atts = _propInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
            _Description = atts.Length > 0 ? ((DescriptionAttribute)atts[0]).Description : null;
            _IsBrowsable = ! _propInfo.Name.Equals("DontShow");
            _DeclaringType = _propInfo.DeclaringType;
            _CanWrite = _propInfo.CanWrite;
            _PropertyType  = _propInfo.PropertyType; 

        }
        /// <summary>
        /// Constructor that takes a property name and its information.
        /// </summary>
        /// <param name="propertyName">Property name</param>
        /// <param name="propInfo">Property information</param>
        public EntityPropertyDescriptor(string propertyName, PropertyInfo propInfo)
            : base(propertyName, new Attribute[]{CategoryAttribute.Default})
        {
            _propInfo = propInfo;
            SetProperties();
        }
    



        /// <summary>
        /// Indicates the category that the property will appear in.  The value will be the value given for the <see cref="CategoryAttribute"/>
        /// attribute for the property, if one exists. "/>.
        /// </summary>
        public override string Category
        {
            get
            {
                return _Category;
            }
        }

        public override string DisplayName
        {
            get
            {
                return string.Empty;
            }
        }




        /// <summary>
        /// Indicates the description for the property.  The value will be the value given for the <see cref="DescriptionAttribute"/>
        /// attribute for the property, if one exists.  If there is no DescriptionAttribute, then this will be null.
        /// </summary>
        public override string Description
        {
            get
            {
                return _Description;
            }
        }


        public override bool IsBrowsable 
        { 
            get 
            { 
                return _IsBrowsable; 
            } 
        }
        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.ComponentType"/>.
        /// </summary>


        public override Type ComponentType 
        { 
            get 
            { 
                return _DeclaringType; 
            } 
        }
        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.IsReadOnly"/>.
        /// </summary>
        public override bool IsReadOnly
        {
            get
            {
                return !_CanWrite;
            }
        }


        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.PropertyType"/>.
        /// </summary>


        public override Type PropertyType 
        { 
            get 
            { 
                return _PropertyType; 
            } 
        }
        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.CanResetValue"/>.
        /// </summary>
        public override bool CanResetValue(object component)
        {
            return true;
        }


        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.GetValue"/>.
        /// </summary>
        public override object GetValue(object component)
        {
            string sName = this.Name;
            PropertyInfo p = null;
            object innerComponent = component;
            while (sName.IndexOf("#") >= 0)
            {
                string sPropertyName = sName.Substring(0, sName.IndexOf("#"));
                p = innerComponent.GetType().GetProperty(sPropertyName);
                if (p != null) //try to get from the property itself.
                {
                    innerComponent = p.GetValue(innerComponent, null);
                }
                sName = sName.Substring(sName.IndexOf("#") + 1);
            }
            p = innerComponent.GetType().GetProperty(sName);
            if (p != null)
            {
                innerComponent = p.GetValue(innerComponent, null);
                return innerComponent;
            }
            return null;
        }


        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.ResetValue"/>.
        /// </summary>
        public override void ResetValue(object component)
        {
        }


        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.SetValue"/>.
        /// </summary>
        public override void SetValue(object component, object value)
        {
            string sName = this.Name;
            PropertyInfo p = null;
            object innerComponent = component;
            while (sName.IndexOf("#") >= 0)
            {
                string sPropertyName = sName.Substring(0, sName.IndexOf("#"));
                p = innerComponent.GetType().GetProperty(sPropertyName);
                innerComponent = p.GetValue(innerComponent, null);
                sName = sName.Substring(sName.IndexOf("#") + 1);
            }
            p = innerComponent.GetType().GetProperty(sName);
            p.SetValue(innerComponent, value, null);
        }


        /// <summary>
        /// Implementation of abstract <see cref="PropertyDescriptor.ShouldSerializeValue"/>.
        /// </summary>
        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }


        public override object GetEditor(Type editorBaseType)
        {
            if (editorBaseType == null)
                return null;
            object[] atts = _propInfo.GetCustomAttributes(typeof(EditorAttribute), true);
            foreach (EditorAttribute editatt in atts)
            {
                if (editatt.EditorBaseTypeName == editorBaseType.AssemblyQualifiedName)
                {
                    Type editortype = Type.GetType(editatt.EditorTypeName);
                    return Activator.CreateInstance(editortype);
                }
            }
            return base.GetEditor(editorBaseType);
        }

        protected override void FillAttributes(IList attList)
        {
            base.FillAttributes(attList);


            object[] atts = _propInfo.GetCustomAttributes(typeof(RefreshPropertiesAttribute), true);
            foreach (Attribute att in atts)
            {
                attList.Add(att);
            }
            atts = _propInfo.GetCustomAttributes(typeof(TypeConverterAttribute), true);
            foreach (Attribute att in atts)
            {
                attList.Add(att);
            }
        }
    
    }
}